/*
 * CAudioFrameSource.cpp
 *
 *  Created on: 2019年1月15日
 *      Author: terry
 */

#include "CAudioFrameSource.h"
#include "CAudioFrameReader.h"
#include "AVFramePtr.h"

#include "CLog.h"
#include "TimeHelper.h"

namespace av
{

CAudioFrameSource::CAudioFrameSource():
    m_reader(new CAudioFrameReader()),
    m_sink(),
    m_url(),
    m_params(),
    m_channels(),
    m_sampleRate(),
    m_sampleFormat(),
    m_offsetPts(),
    m_lastPts()
{
}

CAudioFrameSource::~CAudioFrameSource()
{
    close();
}

void CAudioFrameSource::setSink(AVFrameSink* sink)
{
    m_sink = sink;
}

bool CAudioFrameSource::open(const std::string& url, const std::string& params, int channels, int sampleRate, AVSampleFormat fmt)
{
    if (isOpen())
    {
        close();
    }

    m_url = url;
    m_params = params;
    m_channels = channels;
    m_sampleRate = sampleRate;
    m_sampleFormat = fmt;

    return start();
}

void CAudioFrameSource::close()
{
    if (isRunning())
    {
        stop();
    }

    if (m_reader)
    {
        m_reader->close();
    }

    m_offsetPts = 0;
    m_lastPts = 0;
}

bool CAudioFrameSource::isOpen()
{
    return isRunning();
}

bool CAudioFrameSource::getFormat(int& channels, int& sampleRate, AVSampleFormat& fmt)
{
    channels = m_channels;
    sampleRate = m_sampleRate;
    fmt = m_sampleFormat;

    return true;
}

int CAudioFrameSource::run()
{
    while (!m_canExit)
    {
        if (m_reader->isOpen())
        {
            while (!m_canExit)
            {
                AVFramePtr frame = createAVFrame();
                int rc = m_reader->read(frame.get());
                if (rc == 0)
                {
                    int64_t pts = frame->pts + m_offsetPts;
                    m_lastPts = pts;

                    frame->pts = pts;

                    TimePoint curPoint(util::TimeHelper::getClock(), pts * AV_TIME_BASE / m_sampleRate);
                    if (!m_timePoint.isSet())
                    {
                        m_timePoint = curPoint;
                    }
                    else
                    {
                        int64_t duration = curPoint.getOffset(m_timePoint);

						//CLog::info("clock: %lld, pts: %lld, duration: %d\n", curPoint.m_clock, curPoint.m_pts, (int)duration/1000 );

                        if (duration > 1000 * 5)
                        {
                            av_usleep(duration);
                        }
                    }

                    fireFrame(frame.get());
                }
                else if (rc == AVERROR(EAGAIN))
                {
                    // pass
                }
                else
                {
                    m_reader->close();

                    m_offsetPts = m_lastPts;

                    m_timePoint.reset();

					break;
                }
            }
        }
        else
        {
            if (!m_reader->open(m_url, m_params, m_channels, m_sampleRate, m_sampleFormat))
            {
                CLog::warning("failed to open %s\n", m_url.c_str());
                m_event.timedwait(1000);
            }
        }
    }
    return 0;
}

void CAudioFrameSource::doStop()
{
    m_event.post();
}

void CAudioFrameSource::fireFrame(AVFrame* frame)
{
    if (m_sink)
    {
        m_sink->onFrame(frame);
    }
}



} /* namespace av */
